package com.campus.schedule.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class RedisService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 写入缓存 hash
     *
     * @param h  key
     * @param hk field
     * @param hv value
     */
    public void setHash(String h, String hk, String hv) {
        redisTemplate.opsForHash().put(h, hk, hv);
    }

    /**
     *
     */
    public <T, K> void setHashBatch(String h, Map<T, K> map) {
        redisTemplate.opsForHash().putAll(h, map);
    }

    /**
     * 获取缓存 hash
     *
     * @param h
     * @param obj
     * @param cls
     * @param <T>
     * @return
     */
    public <T> T getHash(String h, Object obj, Class<T> cls) {
        Object o = redisTemplate.opsForHash().get(h, obj);
        try {
            return objectMapper.readValue(o.toString(), cls);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }


    public Map<Object, Object> getHashAll(String h) {
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(h);
        return entries;
    }

    /**
     * 判断hash结构中是否包含某字段(hexists)
     *
     * @param key
     * @param field
     * @return
     */
    public Boolean hexists(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 获取hash结构中指定key的value，可以是多个fields
     *
     * @param key
     * @param fields
     * @return
     */
    public List<Object> hmget(String key, List<Object> fields) {
        List<Object> list = redisTemplate.opsForHash().multiGet(key, fields);
        return list;
    }

    public Object hget(String key, Object field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, String value) {
        boolean result = false;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public boolean setToJson(final String key, Object value) throws JsonProcessingException {
        return set(key, objectMapper.writeValueAsString(value));
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key
     * @param value
     * @param expireTime 秒
     * @return
     */
    public boolean set(final String key, String value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取剩余过期时间
     *
     * @param key
     * @return 剩余过期时间，单位秒
     */
    public Long getTtl(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 设置值，不改变 TTL
     *
     * @param key
     * @param newValue
     */
    public void setWithoutChangingTtl(String key, String newValue) {
        // 获取当前 TTL（秒）
        Long ttl = getTtl(key);

        if (ttl == null || ttl < 0) {
            // 如果 key 不存在或没有设置 TTL，直接 SET
            redisTemplate.opsForValue().set(key, newValue);
        } else {
            // 如果 key 有 TTL，则 SET + 重新设置原 TTL
            redisTemplate.opsForValue().set(key, newValue, ttl, TimeUnit.SECONDS);
        }
    }

    public boolean setToJson(final String key, Object value, Long expireTime) throws JsonProcessingException {
        return set(key, objectMapper.writeValueAsString(value), expireTime);
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key
     * @param value
     * @param expireTime 秒
     * @return
     */
    public boolean setNX(final String key, String value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            result = operations.setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public boolean setNXToJson(final String key, Object value, Long expireTime) throws JsonProcessingException {
        return setNX(key, objectMapper.writeValueAsString(value), expireTime);
    }

    public boolean expire(final String key, long expireTime) {
        return redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    public void removePattern(final String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0)
            redisTemplate.delete(keys);
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public String get(final String key) {
        String result = null;
        ValueOperations<String, String> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }

    public <T> T getFromJson(final String key, Class<T> cls) {
        Object object = get(key);
        if (object == null) {
            return null;
        }
        try {
            return objectMapper.readValue(object.toString(), cls);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("解析缓存数据失败");
        }
    }

    public <T> T getFromJson(final String key, TypeReference<T> typeRef) {
        Object object = get(key);
        if (object == null) {
            return null;
        }
        try {
            return objectMapper.readValue(object.toString(), typeRef);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("解析缓存数据失败");
        }
    }

    // ==================== 延时队列相关方法 ====================

    /**
     * 添加延时任务到有序集合
     *
     * @param queueKey 队列key
     * @param taskData 任务数据
     * @param executeTime 执行时间戳（毫秒）
     */
    public void addDelayTask(String queueKey, String taskData, long executeTime) {
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
        zSetOps.add(queueKey, taskData, executeTime);
    }

    /**
     * 添加延时任务到有序集合（JSON格式）
     *
     * @param queueKey 队列key
     * @param taskData 任务数据对象
     * @param executeTime 执行时间戳（毫秒）
     */
    public void addDelayTaskToJson(String queueKey, Object taskData, long executeTime) throws JsonProcessingException {
        String jsonData = objectMapper.writeValueAsString(taskData);
        addDelayTask(queueKey, jsonData, executeTime);
    }

    /**
     * 获取到期的任务（分数小于等于当前时间戳）
     *
     * @param queueKey 队列key
     * @param currentTime 当前时间戳（毫秒）
     * @param count 获取数量
     * @return 到期的任务集合
     */
    public Set<String> getExpiredTasks(String queueKey, long currentTime, long count) {
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
        return zSetOps.rangeByScore(queueKey, 0, currentTime, 0, count);
    }

    /**
     * 获取到期的任务并解析为指定类型
     *
     * @param queueKey 队列key
     * @param currentTime 当前时间戳（毫秒）
     * @param count 获取数量
     * @param cls 目标类型
     * @return 解析后的任务列表
     */
    public <T> List<T> getExpiredTasksFromJson(String queueKey, long currentTime, long count, Class<T> cls) {
        Set<String> tasks = getExpiredTasks(queueKey, currentTime, count);
        return tasks.stream().map(task -> {
            try {
                return objectMapper.readValue(task, cls);
            } catch (JsonProcessingException e) {
                throw new IllegalArgumentException("解析延时任务数据失败: " + task, e);
            }
        }).collect(java.util.stream.Collectors.toList());
    }

    /**
     * 删除延时任务
     *
     * @param queueKey 队列key
     * @param taskData 任务数据
     * @return 删除的数量
     */
    public Long removeDelayTask(String queueKey, String taskData) {
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
        return zSetOps.remove(queueKey, taskData);
    }

    /**
     * 删除延时任务（JSON格式）
     *
     * @param queueKey 队列key
     * @param taskData 任务数据对象
     * @return 删除的数量
     */
    public Long removeDelayTaskFromJson(String queueKey, Object taskData) throws JsonProcessingException {
        String jsonData = objectMapper.writeValueAsString(taskData);
        return removeDelayTask(queueKey, jsonData);
    }

    /**
     * 批量删除已处理的延时任务
     *
     * @param queueKey 队列key
     * @param tasks 要删除的任务集合
     * @return 删除的数量
     */
    public Long removeDelayTasks(String queueKey, Set<String> tasks) {
        if (tasks == null || tasks.isEmpty()) {
            return 0L;
        }
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
        return zSetOps.remove(queueKey, tasks.toArray());
    }

    /**
     * 获取延时队列中任务的数量
     *
     * @param queueKey 队列key
     * @return 任务数量
     */
    public Long getDelayQueueSize(String queueKey) {
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
        return zSetOps.zCard(queueKey);
    }

    /**
     * 检查延时任务是否存在
     *
     * @param queueKey 队列key
     * @param taskData 任务数据
     * @return 任务的分数（执行时间），如果不存在返回null
     */
    public Double getDelayTaskScore(String queueKey, String taskData) {
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
        return zSetOps.score(queueKey, taskData);
    }

}
